package com.jh.paymentchannel.pojo;

import cn.jh.common.core.support.jpa.Jsr310Converters;
import cn.jh.common.utils.MathKt;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.common.base.MoreObjects;
import com.jh.paymentchannel.payment.OrderType;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.jpa.domain.AbstractPersistable;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.function.Function;


/**
 * {@code PaymentOrder} 成交交易统计。
 *
 * @author Fuchun
 * @since 1.0
 */
@Entity
@Table(name = "t_payment_order_statistic")
public class PaymentOrderStatistic extends AbstractPersistable<Long> {

    /** 用户id */
    @Column(name = "user_id")
    private Long userId;

    /** 成功交易笔数。*/
    @Column(name = "deal_number")
    private Integer dealNumber;

    /** 交易金额总和 */
    @Column(name = "total_amount")
    private BigDecimal totalAmount;

    /** 用户本周总分润金额。*/
    @Column(name = "total_profit")
    private BigDecimal totalProfit;

    @Column(name = "order_type")
    @Enumerated(EnumType.STRING)
    private OrderType orderType;

    /**
     * 计算分润时的利率配置快照（JSON 格式字符串）。
     * 重新计算统计，或者追述数据时，以防止分润利率配置变更引起的统计计算错误 。
     */
    @Column(name = "rate_props", length = 500)
    private String rateProps;

    /** 统计周起始日期（周一）。*/
    @Column(name = "from_date", length = 10)
    @Convert(converter = Jsr310Converters.LocalDateConverter.class)
    private LocalDate fromDate;

    /** 统计周结束日期（周日）。*/
    @Column(name = "to_date", length = 10)
    @Convert(converter = Jsr310Converters.LocalDateConverter.class)
    private LocalDate toDate;

    /** 统计周计数（自然周）。 */
    @Column(name = "week_of_year")
    private Integer weekOfYear;

    /** 分润是否已转移至 User.PaymentProfit */
    @Column(name = "is_transferred")
    private boolean transferred;

    /** 记录生成日期。*/
    @Column(name = "create_time")
    @Convert(converter = Jsr310Converters.LocalDateTimeConverter.class)
    private LocalDateTime createTime;

    public PaymentOrderStatistic() {
        super();
    }

    public PaymentOrderStatistic(Long userId, OrderType orderType) {
        super();
        this.userId = userId;
        this.orderType = orderType;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public Integer getDealNumber() {
        return dealNumber;
    }

    public void setDealNumber(Integer dealNumber) {
        this.dealNumber = dealNumber;
    }

    public BigDecimal getTotalAmount() {
        return totalAmount;
    }

    public void setTotalAmount(BigDecimal totalAmount) {
        this.totalAmount = totalAmount;
    }

    public BigDecimal getTotalProfit() {
        return totalProfit;
    }

    public void setTotalProfit(BigDecimal totalProfit) {
        this.totalProfit = totalProfit;
    }

    public OrderType getOrderType() {
        return orderType;
    }

    public void setOrderType(OrderType orderType) {
        this.orderType = orderType;
    }

    public String getRateProps() {
        return rateProps;
    }

    public void setRateProps(String rateProps) {
        this.rateProps = rateProps;
    }

    public LocalDate getFromDate() {
        return fromDate;
    }

    public void setFromDate(LocalDate fromDate) {
        this.fromDate = fromDate;
    }

    public LocalDate getToDate() {
        return toDate;
    }

    public void setToDate(LocalDate toDate) {
        this.toDate = toDate;
    }

    public Integer getWeekOfYear() {
        return weekOfYear;
    }

    public void setWeekOfYear(Integer weekOfYear) {
        this.weekOfYear = weekOfYear;
    }

    public boolean isTransferred() {
        return transferred;
    }

    public void setTransferred(boolean transferred) {
        this.transferred = transferred;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    // -- Start: build methods ----

    public void addAmountAndProfit(@NotNull UserTurnover turnover, @NotNull BigDecimal profitRate) {
        dealNumber = MathKt.add(dealNumber, turnover.getDealNumber());
        totalAmount = MathKt.add(totalAmount, turnover.getTotalAmount());
        totalProfit = MathKt.add(totalProfit,
                turnover.getTotalAmount().multiply(profitRate).setScale(2, RoundingMode.DOWN));
    }

    public void addAmountAndProfit(PaymentOrder order, BigDecimal profitRate) {
        dealNumber += 1;
        totalAmount = MathKt.add(totalAmount, order.getTotalFee());
        totalProfit = MathKt.add(totalProfit,
                order.getTotalFee().multiply(profitRate).setScale(2, RoundingMode.HALF_UP));
    }

    public PaymentOrderStatistic init() {
        dealNumber = 0;
        totalAmount = BigDecimal.ZERO;
        totalProfit = BigDecimal.ZERO;
        transferred = false;
        return this;
    }

    // -- End: build methods ----

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("id", getId())
                .add("userId", userId)
                .add("dealNumber", dealNumber)
                .add("totalAmount", totalAmount)
                .add("totalProfit", totalProfit)
                .add("orderType", orderType)
                .add("fromDate", fromDate)
                .add("toDate", toDate)
                .add("weekOfYear", weekOfYear)
                .add("transferred", transferred)
                .add("createTime", createTime)
                .toString();
    }
}
